const isObject = (val) => val !== null && typeof val === "object";
const isArray = (val) => Object.prototype.toString.call(val) === '[object Array]';
const hasOwn = (obj, key) => Object.prototype.hasOwnProperty.call(obj, key);

const convert = (target) => isObject(target) ? reactive(target) : target;

/**
 * 响应式处理对象或数组
 * 1. 判断参数，如果不是对象或数组直接返回。
 * 2. proxy 代理 target。
 *    get 收集依赖，如果属性值是对象，需要递归处理。返回处理后的属性值。
 *    set 属性值变化，触发更新。如果新属性值是对象，需递归处理。返回boolean。
 *    deleteProperty 对象属性存在，成功删除后，触发更新。返回boolean。
 * @param { object, array } target 
 * @return { proxy }
 */
export function reactive(target) {
  if (!isObject(target)) {
    return target;
  }

  const handler = {
    get(target, key, receiver) {
      // 收集依赖
      track(target, key);
      const result = Reflect.get(target, key, receiver);
      return convert(result);
    },
    set(target, key, value, receiver) {
      const oldValue = Reflect.get(target, key, receiver);
      if (value === oldValue) return true
    
      const result = Reflect.set(target, key, convert(value), receiver);
      // 触发更新
      trigger(target, key);
      return result;
    },
    deleteProperty(target, key, receiver) {
        const hasKey = hasOwn(target, key);
        const result = Reflect.deleteProperty(target, key, receiver);
        if(hasKey && result) {
            // 触发更新
            trigger(target, key);
            console.log('delete', key);
        }
        return result;
    }
  };

  return new Proxy(target, handler);
}

/**
 * watchEffect 底层调用的方法
 * 在callback() 访问响应式对象属性，收集依赖
 * @param { function } callback 
 */
let activeEffect = null;
export function effect (callback) {
    activeEffect = callback;
    callback();
    activeEffect = null;
}

/**
 * 收集依赖
 * @param {object, array} target 
 * @param {string} key 
 */
let targetMap = new WeakMap();
export function  track (target, key) {
    if(!activeEffect) return
    let depsMap = targetMap.get(target);
    if(!depsMap) {
        depsMap = new Map();
        targetMap.set(target, depsMap);
    }
    let dep = depsMap.get(key);
    if(!dep) {
        dep = new Set();
        depsMap.set(key, dep);
    }
    dep.add(activeEffect);
}

/**
 * 触发更新
 * 先根据target、key找到回调集合，遍历执行。
 * @param {object, array} target 
 * @param {string} key 
 */
export function trigger (target, key) {
    const depsMap = targetMap.get(target);
    if(!depsMap) return;
    const dep = depsMap.get(key);
    if(!dep) return;
    dep.forEach(cb => {
        cb()
    })
}

/**
 * 对原始数据类型进行响应式处理
 * 1. 如果是带有__v_isRef的对象，直接返回
 * 2. 使用convert对raw进行递归的reactive处理，得到value
 * 3. 返回的对象，__v_isRef = true，value属性 获取会收集依赖，设置会触发更新。
 * @param {string, boolean, number} raw 
 * @return {object}
 */
export function ref (raw) {
    if(isObject(raw) && row.__v_isRef) return

    let value = convert(raw);

    const r = {
        __v_isRef: true,
        get value () {
            track(r, 'value');
            return value;
        },
        set value (newValue) {
            if(newValue !== value) {
                value = convert(newValue);
                trigger(r, 'value')
            }
        }
    }
    
    return r;
}

/**
 * 将 响应式对象 指定的属性，变成ref
 * 1. 返回的对象解构与ref返回的类似
 * 2. 无需收集依赖和触发更新，是因为 proxy 本来是响应式的
 * @param {proxy} proxy proxy实例
 * @param {string} key 
 */
export function toRef (proxy, key) {
    return {
        __v_isRef: true,
        get value () {
            return proxy[key];
        },
        set value (newValue) {
            proxy[key] = newValue;
        }
    }
}

/**
 * 将响应式对象所有属性，变成ref
 * @param {*} proxy 
 */
export function toRefs (proxy) {
    const result = isArray(proxy) ? [] : {};
    for(let key in proxy) {
        result[key] = toRef(proxy, key);
    }
    return result
}

/**
 * 计算属性（缓存/惰性）
 * @param {function} getter 
 */
export function computed (getter) {
    const result = ref();
    effect(() => {
        result.value = getter();
    });
    return result;
}